package com.powernode.driver.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.common.constant.SystemConstant;
import com.powernode.driver.mapper.*;
import com.powernode.driver.properties.TencentProperties;
import com.powernode.driver.service.CosService;
import com.powernode.driver.service.DriverInfoService;
import com.powernode.model.entity.driver.*;
import com.powernode.model.form.driver.DriverFaceModelForm;
import com.powernode.model.form.driver.UpdateDriverAuthInfoForm;
import com.powernode.model.vo.driver.DriverAuthInfoVo;
import com.powernode.model.vo.driver.DriverInfoVo;
import com.powernode.model.vo.driver.DriverLoginVo;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.iai.v20180301.IaiClient;
import com.tencentcloudapi.iai.v20180301.models.*;
import com.tencentcloudapi.common.Credential;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class DriverInfoServiceImpl extends ServiceImpl<DriverInfoMapper, DriverInfo> implements DriverInfoService {
    @Resource
    private WxMaService wxMaService; // 微信小程序服务，用于获取用户session信息
    @Resource
    private DriverSetMapper driverSetMapper; // 配送员设置数据访问层
    @Resource
    private DriverAccountMapper driverAccountMapper; // 配送员账户数据访问层
    @Resource
    private DriverLoginLogMapper driverLoginLogMapper; // 配送员登录日志数据访问层
    @Resource
    private CosService cosService; // COS服务，用于文件上传
    @Resource
    private TencentProperties tencentProperties; // 腾讯云配置属性
    @Resource
    private DriverFaceRecognitionMapper driverFaceRecognitionMapper; // 配送员人脸识别数据访问层

    /**
     * 配送员登录接口
     * 通过微信小程序登录code获取openid，然后查询或创建配送员信息
     * 1.获取openid
     * 2.根据openId查询配送员数据
     * 3.存在 则返回配送员信息
     * 4.不存在 则插入配送员信息 再返回
     * 5.初始化默认接单数据
     * 6.初始化账户数据
     *
     * @param code 微信小程序登录code，用于换取openid
     * @return 配送员ID，如果登录失败则返回null
     */
    @Transactional  // 添加事务管理，确保所有数据库操作要么全部成功，要么全部回滚
    @Override
    public Long login(String code) {
        try {
            // 通过微信小程序服务获取session信息，包含openid
            WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
            // 从session信息中提取openid作为用户唯一标识
            String openid = sessionInfo.getOpenid();

            // 创建查询条件，根据openid查找配送员信息
            LambdaQueryWrapper<DriverInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DriverInfo::getWxOpenId, openid);
            // 执行查询，获取配送员信息
            DriverInfo driverInfo = getOne(queryWrapper);

            // 如果配送员不存在，则创建新的配送员记录
            if (driverInfo == null) {
                // 初始化配送员信息
                driverInfo = new DriverInfo();
                driverInfo.setWxOpenId(openid);  // 设置微信openid
                driverInfo.setStatus(1);  // 设置状态为有效（1表示有效）
                driverInfo.setNickname(String.valueOf(System.currentTimeMillis()));  // 临时昵称，使用当前时间戳
                driverInfo.setAvatarUrl("images/avatar.png");  // 默认头像
                save(driverInfo);  // 保存配送员基本信息

                // 初始化配送员设置信息
                DriverSet driverSet = new DriverSet();
                driverSet.setDriverId(driverInfo.getId());  // 关联配送员ID
                driverSet.setAcceptDistance(new BigDecimal(SystemConstant.ACCEPT_DISTANCE));  // 设置接单距离
                driverSet.setOrderDistance(BigDecimal.ZERO);  // 初始订单距离为0
                driverSet.setIsAutoAccept(0);  // 初始不自动接单（0表示否）
                driverSet.setServiceStatus(0);  // 初始服务状态为离线（0表示离线）
                driverSetMapper.insert(driverSet);  // 保存配送员设置

                // 初始化配送员账户信息
                DriverAccount driverAccount = new DriverAccount();
                driverAccount.setDriverId(driverInfo.getId());  // 关联配送员ID
                driverAccountMapper.insert(driverAccount);  // 保存配送员账户

                // 记录配送员登录日志
                DriverLoginLog driverLoginLog = new DriverLoginLog();
                driverLoginLog.setDriverId(driverInfo.getId());  // 关联配送员ID
                driverLoginLog.setStatus(true);  // 登录状态为成功
                driverLoginLog.setMsg("登录成功");  // 登录消息
                driverLoginLogMapper.insert(driverLoginLog);  // 保存登录日志

                // 返回新创建的配送员ID
                return driverInfo.getId();
            }
        } catch (WxErrorException e) {
            // 捕获微信服务异常，转换为运行时异常抛出
            throw new RuntimeException(e);
        }
        // 如果配送员已存在，则返回null（注：这里可能需要改进，建议存在时也返回driverInfo.getId()）
        return null;
    }

    /**
     * 获取配送员登录信息
     *
     * @param driverId 配送员唯一标识ID，不能为null
     * @return DriverLoginVo 配送员登录信息视图对象，包含配送员基本信息和面部识别状态
     */
    @Override
    public DriverLoginVo getDriverLoginInfo(Long driverId) {
        // 根据配送员ID从数据库查询配送员详细信息
        DriverInfo driverInfo = getById(driverId);
        // 创建配送员登录信息视图对象
        DriverLoginVo driverLoginVo = new DriverLoginVo();

        // 将配送员实体对象的属性复制到视图对象中
        BeanUtils.copyProperties(driverInfo, driverLoginVo);
        // 检查配送员是否已录入面部识别信息
        boolean isFace = StringUtils.hasText(driverInfo.getFaceModelId());
        // 设置面部识别录入状态到视图对象中
        driverLoginVo.setIsArchiveFace(isFace);

        // 返回完整的配送员登录信息视图对象
        return driverLoginVo;
    }

    /**
     * 获取配送员认证信息
     * 根据配送员ID查询配送员基本信息，并生成包含可访问图片URL的认证信息VO
     *
     * @param driverId 配送员ID
     * @return 配送员认证信息视图对象，包含基本信息和可访问的图片URL
     */
    @Override
    public DriverAuthInfoVo getDriverAuthInfo(Long driverId) {
        // 根据配送员ID查询配送员基本信息
        DriverInfo driverInfo = getById(driverId);
        // 创建认证信息视图对象
        DriverAuthInfoVo driverAuthInfoVo = new DriverAuthInfoVo();
        // 将配送员基本信息复制到视图对象中
        BeanUtils.copyProperties(driverInfo, driverAuthInfoVo);
        // 设置配送员ID
        driverAuthInfoVo.setDriverId(driverId);
        // 生成身份证正面图片的可访问URL（带签名）
        driverAuthInfoVo.setIdcardFrontShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardFrontUrl()));
        // 生成身份证背面图片的可访问URL（带签名）
        driverAuthInfoVo.setIdcardBackShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardBackUrl()));
        // 生成手持身份证图片的可访问URL（带签名）
        driverAuthInfoVo.setIdcardHandShowUrl(cosService.getImageUrl(driverAuthInfoVo.getIdcardHandUrl()));
        return driverAuthInfoVo;
    }

    /**
     * 更新配送员认证信息
     * 将前端提交的表单数据更新到配送员信息表中
     *
     * @param updateDriverAuthInfoForm 配送员认证信息更新表单，包含需要更新的字段数据
     * @return 更新操作是否成功
     */
    @Override
    public Boolean updateDriverAuthInfo(UpdateDriverAuthInfoForm updateDriverAuthInfoForm) {
        // 创建配送员信息实体对象
        DriverInfo driverInfo = new DriverInfo();
        // 将表单数据复制到实体对象中（同名属性自动映射）
        BeanUtils.copyProperties(updateDriverAuthInfoForm, driverInfo);
        // 设置要更新的配送员记录ID
        driverInfo.setId(updateDriverAuthInfoForm.getDriverId());
        // 执行更新操作，根据ID更新配送员信息
        return updateById(driverInfo);
    }

    /**
     * 创建配送员人脸模型
     * 该方法使用腾讯云人脸识别服务为配送员创建人脸特征模型
     *
     * @param driverFaceModelForm 人脸模型表单，包含配送员ID和Base64编码的人脸图片
     * @return Boolean 创建是否成功
     */
    @Override
    public Boolean createDriverFaceModel(DriverFaceModelForm driverFaceModelForm) {
        // 根据配送员ID查询配送员基本信息
        DriverInfo driverInfo = getById(driverFaceModelForm.getDriverId());

        // 创建腾讯云认证凭证，使用SecretId和SecretKey
        Credential credential = new Credential(tencentProperties.getSecretId(), tencentProperties.getSecretKey());
        // 创建人员创建请求对象
        CreatePersonRequest createPersonRequest = new CreatePersonRequest();

        // 设置人员组ID（从配置获取）
        createPersonRequest.setGroupId(tencentProperties.getPersionGroupId());
        // 设置人员ID（使用配送员ID作为唯一标识）
        createPersonRequest.setPersonId(driverInfo.getId().toString());
        // 设置性别（将字符串格式的性别转换为Long类型）
        createPersonRequest.setGender(Long.parseLong(driverInfo.getGender()));
        // 设置质量控制等级：4-最高质量要求
        createPersonRequest.setQualityControl(4L);
        // 设置人员唯一性控制：4-严格的人员去重
        createPersonRequest.setUniquePersonControl(4L);
        // 设置人员姓名
        createPersonRequest.setPersonName(driverInfo.getName());
        // 设置Base64编码的人脸图片数据
        createPersonRequest.setImage(driverFaceModelForm.getImageBase64());

        // 创建腾讯云人脸识别客户端,参数：认证凭证、区域
        IaiClient iaiClient = new IaiClient(credential, tencentProperties.getRegion());
        try {
            // 调用腾讯云API创建人员
            CreatePersonResponse createPersonResponse = iaiClient.CreatePerson(createPersonRequest);
            // 获取返回的人脸ID
            String faceId = createPersonResponse.getFaceId();
            // 如果成功返回人脸ID，则更新配送员信息
            if (StringUtils.hasText(faceId)) {
                driverInfo.setFaceModelId(faceId);
                updateById(driverInfo);
            }
            // 返回操作成功
            return true;
        } catch (TencentCloudSDKException e) {
            // 腾讯云API调用异常，抛出运行时异常
            throw new RuntimeException(e);
        }
    }

    /**
     * 查询配送员的配送的个性设置
     *
     * @param driverId 配送员ID
     * @return 配送员的配送的个性设置
     */
    @Override
    public DriverSet getDriverSet(Long driverId) {
        // 创建Lambda查询包装器，用于构建查询条件
        LambdaQueryWrapper<DriverSet> queryWrapper = new LambdaQueryWrapper<>();
        // 添加查询条件：配送员ID等于指定值
        queryWrapper.eq(DriverSet::getDriverId, driverId);
        // 执行查询，返回符合条件的配送员个性设置
        DriverSet driverSet = driverSetMapper.selectOne(queryWrapper);
        return driverSet;
    }

    /**
     * 检查配送员当天是否已进行人脸识别
     *
     * @param driverId 配送员ID
     * @return true表示当天已完成人脸识别，false表示当天未完成
     */
    @Override
    public Boolean isFaceRecognition(Long driverId) {
        //创建查询条件包装器
        LambdaQueryWrapper<DriverFaceRecognition> queryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件：配送员ID等于传入的driverId
        queryWrapper.eq(DriverFaceRecognition::getDriverId, driverId);
        //添加查询条件：人脸识别日期等于当天（格式：yyyy-MM-dd）
        queryWrapper.eq(DriverFaceRecognition::getFaceDate, new DateTime().toString("yyyy-MM-dd"));
        //查询符合条件的记录数量
        Long count = driverFaceRecognitionMapper.selectCount(queryWrapper);
        //如果数量不为0，则表示当天已进行人脸识别，返回true；否则返回false
        return count != 0;
    }

    /**
     * 验证配送员人脸识别
     *
     * @param driverFaceModelForm 包含配送员ID和人脸图像Base64编码的表单对象
     * @return true表示人脸识别验证成功，false表示验证失败
     */
    @Override
    public Boolean verifyDriverFace(DriverFaceModelForm driverFaceModelForm) {
        try {
            //创建腾讯云API凭证，使用配置的SecretId和SecretKey
            Credential credential = new Credential(tencentProperties.getSecretId(), tencentProperties.getSecretKey());
            //创建HTTP配置对象
            HttpProfile httpProfile = new HttpProfile();
            //设置API端点为腾讯云人脸识别服务
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            //创建客户端配置对象
            ClientProfile clientProfile = new ClientProfile();
            //设置HTTP配置
            clientProfile.setHttpProfile(httpProfile);

            //创建人脸识别客户端实例
            IaiClient iaiClient = new IaiClient(credential, tencentProperties.getRegion(), clientProfile);

            //创建人脸验证请求对象
            VerifyFaceRequest verifyFaceRequest = new VerifyFaceRequest();
            //设置请求参数：Base64编码的人脸图像
            verifyFaceRequest.setImage(driverFaceModelForm.getImageBase64());
            //设置请求参数：配送员ID（转换为字符串）
            verifyFaceRequest.setPersonId(driverFaceModelForm.getDriverId().toString());
            //发送请求并获取响应
            VerifyFaceResponse verifyFaceResponse = iaiClient.VerifyFace(verifyFaceRequest);

            //判断人脸是否匹配成功
            if (verifyFaceResponse.getIsMatch()) {
                //如果人脸匹配成功，进行活体检测
                //Boolean isDetectLiveFace = detectLiveFace(driverFaceModelForm.getImageBase64());
                //如果活体检测也成功
                //if (isDetectLiveFace) {
                //创建配送员人脸识别记录对象
                DriverFaceRecognition driverFaceRecognition = new DriverFaceRecognition();
                //设置配送员ID
                driverFaceRecognition.setDriverId(driverFaceModelForm.getDriverId());
                //设置人脸识别日期为当前时间
                driverFaceRecognition.setFaceDate(new Date());
                //将记录插入数据库
                driverFaceRecognitionMapper.insert(driverFaceRecognition);
                //返回验证成功
                return true;
                //}
            }
        } catch (TencentCloudSDKException e) {
            //捕获腾讯云SDK异常并转换为运行时异常抛出
            throw new RuntimeException(e);
        }
        //验证失败返回false
        return false;
    }

    /**
     * 更新配送员的服务状态
     *
     * @param driverId 配送员ID
     * @param status   服务状态（通常0表示离线/停止服务，1表示在线/开始服务）
     * @return 操作结果，始终返回true表示更新成功
     */
    @Override
    public Boolean updateServiceStatus(Long driverId, Integer status) {
        //创建查询条件包装器
        LambdaQueryWrapper<DriverSet> queryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件：配送员ID等于传入的driverId
        queryWrapper.eq(DriverSet::getDriverId, driverId);
        //创建配送员设置对象，用于封装更新的字段值
        DriverSet driverSet = new DriverSet();
        //设置服务状态为传入的status值
        driverSet.setServiceStatus(status);
        //执行更新操作，根据查询条件更新配送员的服务状态
        driverSetMapper.update(driverSet, queryWrapper);
        //返回true表示更新成功
        return true;
    }

    /**
     * 获取订单相关的配送员信息
     * 该方法用于获取配送员的基本信息，并设置驾驶证驾龄，用于订单匹配等场景
     *
     * @param driverId 配送员ID，用于查询特定配送员的信息
     * @return DriverInfoVo 配送员信息视图对象，包含配送员的基本信息和驾龄等扩展信息
     */
    @Override
    public DriverInfoVo getDriverInfoOrder(Long driverId) {
        // 根据配送员ID查询配送员的详细信息
        DriverInfo driverInfo = getById(driverId);
        // 创建配送员信息视图对象，用于封装返回给前端的数据
        DriverInfoVo driverInfoVo = new DriverInfoVo();
        // 使用BeanUtils工具将driverInfo的属性值复制到driverInfoVo中
        // 这一步会自动映射同名属性
        BeanUtils.copyProperties(driverInfo, driverInfoVo);

        // 以下代码是计算驾驶证驾龄的逻辑，但目前被注释掉了
        // 计算逻辑：当前年份 - 驾驶证发放年份 + 1（+1表示满1年算1年驾龄）
        // Integer driverLicenseAge = new DateTime().getYear() - new DateTime(driverInfo.getDriverLicenseIssueDate()).getYear() + 1;
        // driverInfoVo.setDriverLicenseAge(driverLicenseAge);
        // 临时硬编码设置驾龄为4年，可能是为了测试或临时替代上面被注释掉的逻辑
        driverInfoVo.setDriverLicenseAge(4);

        // 返回封装好的配送员信息视图对象
        return driverInfoVo;
    }

    /**
     * 调用腾讯云API进行活体人脸检测
     *
     * @param imageBase64 Base64编码的人脸图像
     * @return true表示检测到活体人脸，false表示未检测到活体人脸或检测失败
     */
    private Boolean detectLiveFace(String imageBase64) {
        try {
            //创建腾讯云API凭证，使用配置的SecretId和SecretKey
            Credential cred = new Credential(tencentProperties.getSecretId(), tencentProperties.getSecretKey());
            //创建HTTP配置对象
            HttpProfile httpProfile = new HttpProfile();
            //设置API端点为腾讯云人脸识别服务
            httpProfile.setEndpoint("iai.tencentcloudapi.com");
            //创建客户端配置对象
            ClientProfile clientProfile = new ClientProfile();
            //设置HTTP配置
            clientProfile.setHttpProfile(httpProfile);
            //创建人脸识别客户端实例
            IaiClient client = new IaiClient(cred, tencentProperties.getRegion(), clientProfile);
            //创建活体检测请求对象
            DetectLiveFaceRequest req = new DetectLiveFaceRequest();
            //设置请求参数：Base64编码的人脸图像
            req.setImage(imageBase64);
            //发送请求并获取响应
            DetectLiveFaceResponse resp = client.DetectLiveFace(req);
            //打印响应结果到控制台
            System.out.println(DetectLiveFaceResponse.toJsonString(resp));
            //判断是否检测到活体人脸
            if (resp.getIsLiveness()) {
                return true;
            }
        } catch (TencentCloudSDKException e) {
            //捕获并打印腾讯云SDK异常
            System.out.println(e.toString());
        }
        //检测失败或未检测到活体人脸，返回false
        return false;
    }
}